package ru.emms.codemeter.metrics.calculators;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.PsiComment;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import ru.emms.codemeter.metrics.MetricUtils;

import java.util.Collection;

/**
 * Created by IntelliJ IDEA.
 * User: dima.sam
 * Date: 06.08.2010
 * Time: 12:49:50
 * Вычисление метрики "Количество строк кода без комментариев и пустых строк"
 */
public class SourceInstructionsMetricCalculator
        implements MetricCalculator {

    private Logger log = Logger.getInstance(getClass().getName());
    private static final String NAME = "SI";

    /**
     * возвращает количество строк без комментариев и пустых строк списка классов sources
     *
     * @param sources коллекция исходников
     */
    @Override
    public Long calculate(Collection<PsiFile> sources) throws IllegalArgumentException {
        Long lineCount = 0L;
        //цикл по исходникам модуля
        for (PsiElement source : sources) {
            lineCount += calculateSourceInstraction(source);
        }
        log.debug("количество строк кода без комментариев = " + lineCount);
        return lineCount;
    }

    /**
     * Число строк без комментариев и пустых строк в исходнике элемента
     *
     * @param element анализируемый элемент исходника
     *
     * @return число строк без комментариев и пустых строк
     */
    private int calculateSourceInstraction(PsiElement element) {
        PsiElement modifiedElement = element.copy();
        deleteComments(modifiedElement);
        String modifiedSource = modifiedElement.getText();
        modifiedSource = removeEmptyLines(modifiedSource);
        return MetricUtils.linesCount(modifiedSource);
    }

    /**
     * Рекурсивное удаление всех вложенных комментариев из элемента
     *
     * @param element модифицируемый элемент.
     */
    private void deleteComments(PsiElement element) {
        if(element instanceof PsiComment) {
            element.delete();
        } else {
            for (PsiElement child : element.getChildren()) {
                deleteComments(child);
            }
        }
    }

    /**
     * Удаление пустых строк
     *
     * @param source исходная строка
     *
     * @return исходник без пустых строк
     */
    private static String removeEmptyLines(String source) {
        return source.replaceAll("\n[\\s]*\n", "\n").trim();
    }

    public String toString() {
        return NAME;
    }
}
